package data.platform.common.stream.aggregates;

import data.platform.common.stream.valueobjects.WindowType;
import data.platform.common.util.DateUtil;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.Setter;

import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.temporal.TemporalAdjusters;
import java.util.Objects;
import java.util.concurrent.TimeUnit;

@Getter
@Setter
@AllArgsConstructor
public class TimeWindow {

    // include [
    private final long start;

    // exclude }
    private final long end;

    public static TimeWindow getTimeWindow(WindowType windowType, int windowSize, LocalDateTime eventTime) {
        switch (windowType) {
            case MINUTE:
                return getMinuteTimeWindow(windowSize, eventTime);
            case HOUR:
                return getHourTimeWindow(windowSize, eventTime);
            case DAY:
                return getDayTimeWindow(windowSize, eventTime);
            case MONTH:
                return getMonthTimeWindow(windowSize, eventTime);
            default:
                return getYearTimeWindow(windowSize, eventTime);
        }
    }

    public static TimeWindow getMinuteTimeWindow(int minuteSize, LocalDateTime eventTime) {
        long timestamp = DateUtil.getDateOfLocalDateTime(eventTime).getTime();
        long windowSize = TimeUnit.MINUTES.toMillis(minuteSize);
        long startTime = timestamp - (timestamp + windowSize) % windowSize;
        long endTime = startTime + windowSize;
        return new TimeWindow(startTime, endTime);
    }

    public static TimeWindow getHourTimeWindow(int hourSize, LocalDateTime eventTime) {
        long timestamp = DateUtil.getDateOfLocalDateTime(eventTime).getTime();
        long windowSize = TimeUnit.HOURS.toMillis(hourSize);
        long startTime = timestamp - (timestamp + windowSize) % windowSize;
        long endTime = startTime + windowSize;
        return new TimeWindow(startTime, endTime);
    }

    public static TimeWindow getDayTimeWindow(int daySize, LocalDateTime eventTime) {
        LocalDateTime startTime = eventTime.toLocalDate().atTime(LocalTime.MIN);
        LocalDateTime endTime = eventTime.plusDays(daySize).toLocalDate().atTime(LocalTime.MIN);
        return new TimeWindow(DateUtil.getDateOfLocalDateTime(startTime).getTime(), DateUtil.getDateOfLocalDateTime(endTime).getTime());
    }

    public static TimeWindow getMonthTimeWindow(int monthSize, LocalDateTime eventTime) {
        LocalDateTime startTime = eventTime.with(TemporalAdjusters.firstDayOfMonth()).toLocalDate().atTime(LocalTime.MIN);
        LocalDateTime endTime = eventTime.plusMonths(monthSize).with(TemporalAdjusters.firstDayOfMonth()).toLocalDate().atTime(LocalTime.MIN);
        return new TimeWindow(DateUtil.getDateOfLocalDateTime(startTime).getTime(), DateUtil.getDateOfLocalDateTime(endTime).getTime());
    }

    public static TimeWindow getYearTimeWindow(int yearSize, LocalDateTime eventTime) {
        LocalDateTime startTime = eventTime.with(TemporalAdjusters.firstDayOfYear()).toLocalDate().atTime(LocalTime.MIN);
        LocalDateTime endTime = eventTime.plusYears(yearSize).with(TemporalAdjusters.firstDayOfYear()).toLocalDate().atTime(LocalTime.MIN);
        return new TimeWindow(DateUtil.getDateOfLocalDateTime(startTime).getTime(), DateUtil.getDateOfLocalDateTime(endTime).getTime());
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        TimeWindow that = (TimeWindow) o;
        return start == that.start && end == that.end;
    }

    @Override
    public int hashCode() {
        return Objects.hash(start, end);
    }
}
